package com.bailun91.auth.jwt;


import com.bailun91.auth.dto.UserAgentDto;
import com.bailun91.auth.entity.User;
import com.bailun91.auth.util.Constants;
import com.bailun91.auth.util.UserAgentUtil;
import io.jsonwebtoken.*;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

public class JwtUtil {

    /**
     * 用户登录成功后生成Jwt
     * 使用Hs256算法  私匙使用用户密码
     * @return
     */
    public static String createJWT(String username, HttpServletRequest httpServletRequest) {
        //指定签名的时候使用的签名算法，也就是header那部分，jjwt已经将这部分内容封装好了。
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        //生成JWT的时间
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        UserAgentDto userAgent = UserAgentUtil.paseUserAgent(httpServletRequest);

        //创建payload的私有声明（根据特定的业务需要添加，如果要拿这个做验证，一般是需要和jwt的接收方提前沟通好验证方式的）
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put(Constants.JWT_ACCOUNT,username);
        claims.put(Constants.JWT_DEVINFO,userAgent);

        //下面就是在为payload添加各种标准声明和私有声明了
        //这里其实就是new一个JwtBuilder，设置jwt的body
        JwtBuilder builder = Jwts.builder()
                //如果有私有声明，一定要先设置这个自己创建的私有的声明，这个是给builder的claim赋值，一旦写在标准的声明赋值之后，就是覆盖了那些标准的声明的
                .setClaims(claims)
                //设置jti(JWT ID)：是JWT的唯一标识，根据业务需要，这个可以设置为一个不重复的值，主要用来作为一次性token,从而回避重放攻击。
                .setId(UUID.randomUUID().toString())
                //iat: jwt的签发时间
                .setIssuedAt(now)
                //代表这个JWT的主体，即它的所有人，这个是一个json格式的字符串，可以存放什么userid，roldid之类的，作为什么用户的唯一标志。
                .setSubject(username)
                //设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, Constants.SECRET_KEY);

        Date exp = new Date(nowMillis + Constants.ACCESS_TOKEN_EXP_TIME * 1000);
        //设置过期时间
        builder.setExpiration(exp);

        return builder.compact();
    }


    /**
     * Token的解密
     * @param token 加密后的token
     * @return
     */
    public static Claims parseJWT(String token) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException{
        //签名秘钥，和生成的签名的秘钥一模一样
        String key = Constants.SECRET_KEY;
        //得到DefaultJwtParser
            Claims claims = Jwts.parser()
                    //设置签名的秘钥
                    .setSigningKey(key)
                    //设置需要解析的jwt
                    .parseClaimsJws(token).getBody();
              return claims;
    }

    /**
     * 校验token
     * @param token
     * @return
     */
    public static Boolean VerifyTokenDevInfo(String token,HttpServletRequest request) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, SignatureException, IllegalArgumentException{
        Claims claims = parseJWT(token);

        Map<String, Object> devInfo = (Map<String, Object>) claims.get(Constants.JWT_DEVINFO);
        UserAgentDto userAgent = UserAgentUtil.paseUserAgent(request);
        if (null != devInfo
                && null != userAgent
//                && devInfo.get("ip").equals(userAgent.getIp())
//                && devInfo.get("systemName").equals(userAgent.getSystemName())
//                && devInfo.get("browserName").equals(userAgent.getBrowserName())
//                && devInfo.get("browserVersion").equals(userAgent.getBrowserVersion())
        ){
            return true;
        }
        return false;
    }

    /**
     * 从token获取用户名
     * @param token
     * @return
     */
    public static String getUserNameFromToken(String token){
        try {
            Claims claims = parseJWT(token);
            return (String)claims.get(Constants.JWT_ACCOUNT);
        }catch (Exception e){
            return null;
        }
    }

    /**
     * token是否在有效时间内，即签发时间是否超过了refreshToken的有效时间
     * @param token
     * @return
     */
    public static boolean isTokenActive(String token){
        Date now = Calendar.getInstance().getTime();
        Claims claims = JwtUtil.parseJWT(token);
        Date expires = claims.getIssuedAt();
        long time = Constants.REFRESH_TOKEN_EXP_TIME;
        Date lastActiveDate = new Date(expires.getTime() + time *1000);// token 最后的可用时间
        return lastActiveDate.after(now);
    }
}
